/*
 * OutputStack.java July 2006
 *
 * Copyright (C) 2006, Niall Gallagher <niallg@users.sf.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package wx.xml.simpleframework.xml.stream;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

/**
 * The <code>OutputStack</code> is used to keep track of the nodes
 * that have been written to the document. This ensures that when
 * nodes are written to  the XML document that the writer can tell
 * whether a child node for a given <code>OutputNode</code> can be
 * created. Each created node is pushed, and popped when ended.
 *
 * @author Niall Gallagher
 * @see wx.xml.simpleframework.xml.stream.OutputNode
 */
class OutputStack extends ArrayList<OutputNode> {

    /**
     * Represents the set of nodes that have not been committed.
     */
    private final Set active;

    /**
     * Constructor for the <code>OutputStack</code> object. This is
     * used to create a stack that can be used to keep track of the
     * elements that have been written to the XML document.
     */
    public OutputStack(Set active) {
        this.active = active;
    }

    /**
     * This is used to remove the <code>OutputNode</code> from the
     * top of the output stack. This is used when an element has been
     * ended and the output writer wants to block child creation.
     *
     * @return this returns the node from the top of the stack
     */
    public OutputNode pop() {
        int size = size();

        if (size <= 0) {
            return null;
        }
        return purge(size - 1);
    }

    /**
     * This is used to acquire the <code>OutputNode</code> from the
     * top of the output stack. This is used when the writer wants to
     * determine the current element written to the XML document.
     *
     * @return this returns the node from the top of the stack
     */
    public OutputNode top() {
        int size = size();

        if (size <= 0) {
            return null;
        }
        return get(size - 1);
    }

    /**
     * This is used to acquire the <code>OutputNode</code> from the
     * bottom of the output stack. This is used when the writer wants
     * to determine the root element for the written XML document.
     *
     * @return this returns the node from the bottom of the stack
     */
    public OutputNode bottom() {
        int size = size();

        if (size <= 0) {
            return null;
        }
        return get(0);
    }

    /**
     * This method is used to add an <code>OutputNode</code> to the
     * top of the stack. This is used when an element is written to
     * the XML document, and allows the writer to determine if a
     * child node can be created from a given output node.
     *
     * @param value this is the output node to add to the stack
     */
    public OutputNode push(OutputNode value) {
        active.add(value);
        add(value);
        return value;
    }

    /**
     * The <code>purge</code> method is used to purge a match from
     * the provided position. This also ensures that the active set
     * has the node removed so that it is no longer relevant.
     *
     * @param index the index of the node that is to be removed
     * @return returns the node removed from the specified index
     */
    public OutputNode purge(int index) {
        OutputNode node = remove(index);

        if (node != null) {
            active.remove(node);
        }
        return node;
    }

    /**
     * This is returns an <code>Iterator</code> that is used to loop
     * through the ouptut nodes from the top down. This allows the
     * node writer to determine what <code>Mode</code> should be used
     * by an output node. This reverses the iteration of the list.
     *
     * @return returns an iterator to iterate from the top down
     */
    public Iterator<OutputNode> iterator() {
        return new Sequence();
    }

    /**
     * The is used to order the <code>OutputNode</code> objects from
     * the top down. This is basically used to reverse the order of
     * the linked list so that the stack can be iterated within a
     * for each loop easily. This can also be used to remove a node.
     *
     * @author Niall Gallagher
     */
    private class Sequence implements Iterator<OutputNode> {

        /**
         * The cursor used to acquire objects from the stack.
         */
        private int cursor;

        /**
         * Constructor for the <code>Sequence</code> object. This is
         * used to position the cursor at the end of the list so the
         * last inserted output node is the first returned from this.
         */
        public Sequence() {
            this.cursor = size();
        }

        /**
         * Returns the <code>OutputNode</code> object at the cursor
         * position. If the cursor has reached the start of the list
         * then this returns null instead of the first output node.
         *
         * @return this returns the node from the cursor position
         */
        public OutputNode next() {
            if (hasNext()) {
                return get(--cursor);
            }
            return null;
        }

        /**
         * This is used to determine if the cursor has reached the
         * start of the list. When the cursor reaches the start of
         * the list then this method returns false.
         *
         * @return this returns true if there are more nodes left
         */
        public boolean hasNext() {
            return cursor > 0;
        }

        /**
         * Removes the match from the cursor position. This also
         * ensures that the node is removed from the active set so
         * that it is not longer considered a relevant output node.
         */
        public void remove() {
            purge(cursor);
        }
    }
}
